/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package srctopdf;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.swing.filechooser.FileNameExtensionFilter;
import org.apache.pdfbox.TextToPDF;
import org.apache.pdfbox.exceptions.COSVisitorException;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.interactive.documentnavigation.destination.PDPageFitWidthDestination;
import org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline.PDDocumentOutline;
import org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline.PDOutlineItem;
import org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline.PDOutlineNode;

/**
 *
 * @author BCP
 */
public final class BCPSrcToPDF {

    public static final String[] SUPPORTED_FILE_TYPE = {"c", "s", "S", "h", "inc","java"};
    public static final FileNameExtensionFilter FILTER =
            new FileNameExtensionFilter("src files", SUPPORTED_FILE_TYPE);
    public static final int ID_ERROR = -1;
    public static final ExecutorService threadsPool = Executors.newCachedThreadPool();

    public static int transformSrcToSinglePDF(BCPSrcToPDFCallback callback,
            File[] fps, String dst, boolean singlepdf) {
        if (fps == null || fps.length == 0) {
            return ID_ERROR;
        }
        STP stp = null;
        int ret = ID_ERROR;

        try {
            stp = new STP(callback, fps, fps[0].getParent(), dst, singlepdf);
            ret = stp.hashCode();
            threadsPool.execute(stp);
            return ret;
        } catch (IOException ex) {
            ret = stp.hashCode();
            callback.result(ret, false);
            ex.printStackTrace();
            return ret;
        }
    }

    private static class STP implements Runnable {

        private File[] initFiles;
        private String dst;
        private String org;
        private TextToPDF ttp;
        private boolean single;
        private PDDocumentOutline outline;
        private PDOutlineNode parent;
        private PDDocument doc;
        private BCPSrcToPDFCallback callback;
        private LinkedList<PDOutlineNode> parentsStack;

        public STP(BCPSrcToPDFCallback uc, File[] fps, String uorg, String udst,
                boolean usingle) throws IOException {
            ttp = new TextToPDF();
            initFiles = fps;
            org = uorg;
            dst = udst;
            single = usingle;
            callback = uc;
            if (single) {
                doc = new PDDocument();
                outline = new PDDocumentOutline();
                doc.getDocumentCatalog().setDocumentOutline(outline);
                parent = outline;
                parentsStack = new LinkedList<PDOutlineNode>();
            }
        }

        public void run() {
            try {
                saveAll(initFiles);
                if (single) {
                    outline.openNode();
                    System.out.println(dst + File.separator + "pages" + ".pdf");
                    doc.save(dst + File.separator + "pages" + ".pdf");
                    doc.close();
                }
                callback.result(this.hashCode(), true);
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }

        private void saveAll(File[] fps) throws IOException, COSVisitorException {
            for (File fp : fps) {
                if (fp.isFile() && FILTER.accept(fp)) {
                    save(fp);
                } else if (fp.isDirectory()) {
                    if (single) {
                        PDOutlineItem title = new PDOutlineItem();
                        title.setTitle(fp.getName());
                        parent.appendChild(title);
                        parentsStack.addFirst(parent);
                        parent = title;
                    } else {
                        String rpath = fp.getAbsolutePath().replace(org, "");
                        String s = dst + rpath;
                        File dir = new File(s);
                        dir.mkdirs();
                    }
                    saveAll(fp.listFiles());
                    if (single) {
                        if (!parentsStack.isEmpty()) {
                            parent = parentsStack.pollFirst();
                        }
                    }
                }
            }
        }

        private void save(File fp) throws IOException, COSVisitorException {

            PDDocument tdoc = ttp.createPDFFromText(new BufferedReader(new FileReader(fp)));
            if (single) {
                List<PDPage> pages = tdoc.getDocumentCatalog().getAllPages();
                int beforeNums = doc.getNumberOfPages();
                for (PDPage pg : pages) {
                    doc.importPage(pg);
                }
                tdoc.close();
                //Create the outline item to refer to the first page.
                PDPage firstPage = (PDPage) doc.getDocumentCatalog().getAllPages().get(beforeNums);
                PDOutlineItem firstPageItem = new PDOutlineItem();
                firstPageItem.setTitle(fp.getName());
                PDPageFitWidthDestination dest = new PDPageFitWidthDestination();
                dest.setPage(firstPage);
                firstPageItem.setDestination(firstPage);
                parent.appendChild(firstPageItem);
            } else {
                String s = dst + fp.getAbsolutePath().replace(org, "");
                int dotIndex = s.lastIndexOf(".");
                if (dotIndex != -1) {
                    s = s.substring(0, dotIndex) + "_" + s.substring(dotIndex + 1);
                }
                s = s + ".pdf";
                tdoc.save(s);
                tdoc.close();
            }
        }

        @Override
        public boolean equals(Object obj) {
            if (obj == null) {
                return false;
            }
            if (getClass() != obj.getClass()) {
                return false;
            }
            final STP other = (STP) obj;
            if (!Arrays.deepEquals(this.initFiles, other.initFiles)) {
                return false;
            }
            if ((this.dst == null) ? (other.dst != null) : !this.dst.equals(other.dst)) {
                return false;
            }
            if ((this.org == null) ? (other.org != null) : !this.org.equals(other.org)) {
                return false;
            }
            return true;
        }

        @Override
        public int hashCode() {
            int hash = 5;
            hash = 37 * hash + Arrays.deepHashCode(this.initFiles);
            hash = 37 * hash + (this.dst != null ? this.dst.hashCode() : 0);
            hash = 37 * hash + (this.org != null ? this.org.hashCode() : 0);
            return hash;
        }
    }
}
